// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if NET6_0_OR_GREATER

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ArmCrc = System.Runtime.Intrinsics.Arm.Crc32;

namespace BloomFilter.HashAlgorithms.Internal;

internal partial class Crc32
{
    private static uint UpdateScalarArm64(uint crc, ReadOnlySpan<byte> source)
    {
        Debug.Assert(ArmCrc.Arm64.IsSupported, "ARM CRC support is required.");

        // Compute in 8 byte chunks
        if (source.Length >= sizeof(ulong))
        {
            ref byte ptr = ref MemoryMarshal.GetReference(source);
            int longLength = source.Length & ~0x7; // Exclude trailing bytes not a multiple of 8

            for (int i = 0; i < longLength; i += sizeof(ulong))
            {
                crc = ArmCrc.Arm64.ComputeCrc32(crc,
                    Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref ptr, i)));
            }

            source = source.Slice(longLength);
        }

        // Compute remaining bytes
        for (int i = 0; i < source.Length; i++)
        {
            crc = ArmCrc.ComputeCrc32(crc, source[i]);
        }

        return crc;
    }

    private static uint UpdateScalarArm32(uint crc, ReadOnlySpan<byte> source)
    {
        Debug.Assert(ArmCrc.IsSupported, "ARM CRC support is required.");

        // Compute in 4 byte chunks
        if (source.Length >= sizeof(uint))
        {
            ref byte ptr = ref MemoryMarshal.GetReference(source);
            int intLength = source.Length & ~0x3; // Exclude trailing bytes not a multiple of 4

            for (int i = 0; i < intLength; i += sizeof(uint))
            {
                crc = ArmCrc.ComputeCrc32(crc,
                    Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref ptr, i)));
            }

            source = source.Slice(intLength);
        }

        // Compute remaining bytes
        for (int i = 0; i < source.Length; i++)
        {
            crc = ArmCrc.ComputeCrc32(crc, source[i]);
        }

        return crc;
    }
}
#endif